package user

import (
	"errors"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	uuid "github.com/satori/go.uuid"
	"higame/config"
	"higame/modules/crypto"
	"higame/modules/role"
	"higame/modules/store"
	"higame/modules/utils"
	log2 "higame/routes/log"
	"strconv"
	"strings"
	"time"
)

func validatorForLogin(ctx *gin.Context) (*Schema, error) {
	u := New()
	err := ctx.ShouldBind(u)
	if err != nil {
		errText := err.Error()
		var msgList [] string
		if strings.Contains(errText, "'Username' failed on the 'required' tag") {
			msgList = append(msgList, "用户名不能为空")
		}

		if strings.Contains(errText, "'Password' failed on the 'required' tag") {
			msgList = append(msgList, "密码不能为空")
		}
		if len(msgList) > 0 {
			return nil, errors.New(strings.Join(msgList, "，") + "。")
		}
	}
	now := utils.Timestamp()
	uS, err := FindOneByName(u.Username)

	// 用户名不存在
	if err != nil {
		return nil, err
	}
	log := &log2.LoginLogSchema{
		AddTime:       now,
		Username:      uS.Username,
		LastLoginTime: now,
		LastCheckTime: now,
		IP:            ctx.ClientIP(),
		Success:       false,
		Location:      utils.IP2Region(ctx.ClientIP()),
		Text:          "",
		PcCode:        uS.PcCode,
		ClientID:      uS.ClientID,
	}
	// 验证密码
	if crypto.Md5encrypt(u.Password) != uS.Password {
		errText := "用户密码错误"
		log.Text = errText
		_ = log2.AddOneLoginLog(log)
		return nil, errors.New(errText)
	}

	// 验证是否锁定
	if uS.IsLock {
		errText := "用户被锁定"
		log.Text = errText
		_ = log2.AddOneLoginLog(log)
		return nil, errors.New(errText)
	}
	return uS, nil
}

func validatorForCheck(ctx *gin.Context) (*Schema, error) {
	u := New()
	_ = ctx.ShouldBind(u)

	claims, err := store.GetClaims(ctx)
	if err != nil {
		return nil, errors.New("token解密失败！")
	}
	uS, err := FindOneByName(claims.Audience)

	// 用户名不存在
	if err != nil {
		return nil, err
	}

	// 验证唯一登录
	if u.ClientID != uS.ClientID {
		return nil, errors.New("此用户在其它地方登录！")
	}

	// 验证是否锁定
	if uS.IsLock {
		return nil, errors.New("用户被锁定")
	}

	return uS, nil
}

func validatorForCreate(ctx *gin.Context) (*Schema, error) {
	u := New()
	err := ctx.ShouldBind(u)
	if err != nil {
		errText := err.Error()
		var msgList [] string
		if strings.Contains(errText, "'Username' failed on the 'required' tag") {
			msgList = append(msgList, "用户名不能为空")
		}

		if strings.Contains(errText, "'Password' failed on the 'required' tag") {
			msgList = append(msgList, "密码不能为空")
		}

		if strings.Contains(errText, "'ShareCode' failed on the 'required' tag") {
			msgList = append(msgList, "邀请码不能为空")
		}

		if strings.Contains(errText, "'Username' failed on the 'min' tag") {
			msgList = append(msgList, "用户名不能少于3位")
		}

		if strings.Contains(errText, "'Password' failed on the 'min' tag") {
			msgList = append(msgList, "密码不能少于5位")
		}

		if strings.Contains(errText, "'Username' failed on the 'max' tag") {
			msgList = append(msgList, "用户名不能大于8位")
		}

		if strings.Contains(errText, "'Password' failed on the 'max' tag") {
			msgList = append(msgList, "密码不能大于20位")
		}
		if len(msgList) > 0 {
			return nil, errors.New(strings.Join(msgList, "，") + "。")
		}
	}

	now := utils.Timestamp()
	_, err = FindOneByName(u.Username)

	// 有相同用户
	if err == nil {
		return nil, errors.New("有相同用户")
	}
	return &Schema{
		EXP:           0,
		Alipay:        "",
		Address:       make([]AddressSchema, 0),
		Username:      u.Username,
		Password:      crypto.Md5encrypt(u.Password),
		Avatar:        "",
		Nick:          u.Nick,
		Role:          role.USER,
		ShareCode:     utils.MakeKey(6),
		UUID:          uuid.NewV4().String(),
		Integral:      u.Integral,
		Email:         u.Email,
		IsLock:        false,
		QQ:            u.QQ,
		WX:            u.WX,
		Tel:           u.Tel,
		Tag:           "",
		BdInfo:        u.BdInfo,
		AddTime:       now,
		EndTime:       0,
		PcCode:        "",
		ClientID:      "",
		LastLoginTime: 0,
		LastCheckTime: 0,
	}, nil
}

func validatorForDelete(ctx *gin.Context) (*Schema, error) {
	u := New()
	err := ctx.ShouldBind(u)
	if err != nil {
		errText := err.Error()
		var msgList [] string
		if strings.Contains(errText, "'Username' failed on the 'required' tag") {
			msgList = append(msgList, "用户名不能为空")
		}
		if len(msgList) > 0 {
			return nil, errors.New(strings.Join(msgList, "，") + "。")
		}
	}
	// 不允许修改管理员
	if u.Username == config.AdminName {
		return nil, errors.New("权限不足！")
	}
	return u, nil
}

func validatorForUpdate(ctx *gin.Context) (*Schema, error) {
	u := New()
	err := ctx.ShouldBind(u)
	if err != nil {
		errText := err.Error()
		var msgList [] string
		if strings.Contains(errText, "'Username' failed on the 'required' tag") {
			msgList = append(msgList, "用户名不能为空")
		}

		if len(msgList) > 0 {
			return nil, errors.New(strings.Join(msgList, "，") + "。")
		}
	}
	// 不允许修改管理员
	if u.Username == config.AdminName {
		return nil, errors.New("权限不足！")
	}
	return u, nil
}

func validatorForAddress(ctx *gin.Context) (*AddressSchema, error) {
	v := &AddressSchema{}
	err := ctx.ShouldBind(v)
	if err != nil {
		errText := err.Error()
		var msgList [] string
		if strings.Contains(errText, "'Address' failed on the 'required' tag") {
			msgList = append(msgList, "收货地址不能为空")
		}

		if strings.Contains(errText, "'Tel' failed on the 'required' tag") {
			msgList = append(msgList, "手机不能为空")
		}

		if strings.Contains(errText, "'Name' failed on the 'required' tag") {
			msgList = append(msgList, "收货人不能为空")
		}

		if strings.Contains(errText, "'PostCode' failed on the 'required' tag") {
			msgList = append(msgList, "邮编不能为空")
		}

		if len(msgList) > 0 {
			return nil, errors.New(strings.Join(msgList, "，") + "。")
		}
	}

	return v, nil
}

func generateToken(username string, userId string, roleType role.Type) (token string, err error) {
	claims := jwt.StandardClaims{
		Audience:  username,                                                 // 受众
		ExpiresAt: time.Now().Add(config.JWTExpiresTime * time.Hour).Unix(), // 失效时间
		Id:        userId,                                                   // 编号
		IssuedAt:  time.Now().Unix(),                                        // 签发时间
		Issuer:    strconv.Itoa(int(roleType)),                              // 签发人
		NotBefore: time.Now().Unix(),                                        // 生效时间
		Subject:   "login",                                                  // 主题
	}
	tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return tokenClaims.SignedString([]byte(config.Secret))
}
